package controllers

import auth.AuthActionWrapper
import dao._
import implicits.Implicits._
import models.Tables._
import org.joda.time.DateTime
import play.api.mvc.{AbstractController, ControllerComponents}
import tool.Tool
import utils.Utils

import javax.inject.Inject
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.Future
import upickle.default._
import ujson.play.PlayJson
import io.scalaland.chimney.dsl._
import shared.pojo.Pojo._

import java.io.{ByteArrayInputStream, File}
import javax.imageio.ImageIO
import javax.xml.bind.DatatypeConverter
import auth.AuthTool._
import shared.pojo.Role
import com.softwaremill.quicklens._

/**
 * Created by Administrator on 2019/8/7
 */
class SampleController @Inject()(cc: ControllerComponents)(
  implicit val accountDao: AccountDao,
  implicit val wechatMemberDao: WechatMemberDao,
  implicit val sampleDao: SampleDao,
  implicit val sampleIdDao: SampleIdDao,
  implicit val testDao: TestDao,
  implicit val authAction: AuthActionWrapper,
) extends
  AbstractController(cc) {

  import sample._

  def add = Action.async(parse.multipartFormData) { implicit request =>
    val data = read[SampleInfo](request.body.dataParts("sampleInfo").head)
    val basicInfo = data.transformInto[BasicInfo]
    val freezerInfo = data.transformInto[FreezerInfo]
    Tool.base642Image(data)

    val labAppendDir = Tool.getLabAppendDir(data.id).createDirectoryWhenNoExist
    val files = Tool.filesMoveDir(s"labFiles", labAppendDir)
    val fileNames = files.map(_.getName)
    val lab = data.lab.modify(_.files).setTo(fileNames)
    val row = SampleRow(id = data.id, updateTime = new DateTime(), tests = writeJs(data.tests),
      basicInfo = writeJs(basicInfo), freezerInfo = writeJs(freezerInfo), lab = writeJs(lab))
    sampleDao.insertOrUpdate(row).flatMap { x =>
      Tool.updateTests(List(row))
    }.map { x =>
      val response = ValidData(valid = true)
      Ok(writeJs(response))
    }
  }

  def batchAdd = Action.async(parse.multipartFormData) { implicit request =>
    val samples = read[List[SampleInfo]](request.body.dataParts("samples").head)
    val ids = samples.map(_.id)
    sampleDao.selectAll.flatMap { allSamples =>
      val sampleMap = allSamples.filter { x =>
        ids.contains(x.id)
      }.map { x =>
        (x.id, x)
      }.toMap
      val rows = samples.filter { data =>
        (!sampleMap.isDefinedAt(data.id)) ||
          (sampleMap.isDefinedAt(data.id) && !sampleMap(data.id).locked &&
            sampleMap(data.id).updateTime.isBefore(new DateTime(data.modifyTime)))
      }.map { data =>
        val basicInfo = data.transformInto[BasicInfo]
        val freezerInfo = data.transformInto[FreezerInfo]
        val lab = sampleMap.get(data.id).map(_.lab).getOrElse(ujson.Obj())
        SampleRow(id = data.id, updateTime = new DateTime(), tests = writeJs(data.tests),
          freezerInfo = writeJs(freezerInfo),
          basicInfo = writeJs(basicInfo), lab = lab)
      }
      val newAllSamples = allSamples.filterNot { x =>
        rows.map(_.id).contains(x.id)
      } ::: rows
      val freezerRepeats = newAllSamples.map { row =>
        (row.id, row.freezerInfo.transform(reader[FreezerInfo]))
      }.filter { case (id, info) =>
        List(info.freezerNumber, info.freezerLayerNumber, info.rowNumber, info.colNumber).forall(_.nonBlank)
      }.map { case (id, info) =>
        (info.freezerNumber, info.freezerLayerNumber, info.rowNumber, info.colNumber) -> id
      }.groupSeqMap.filter { case (k, vs) =>
        vs.size > 1
      }
      if (freezerRepeats.nonEmpty) {
        Future {
          val sampleIdStr = freezerRepeats.head._2.mkString("、")
          ValidData(valid = false, message = s"样本编号( ${sampleIdStr}) 冰箱信息不能一样！")
        }
      } else {
        rows.foreach { row =>
          Tool.imagesMove(row.id)
        }
        sampleDao.updates(rows).flatMap { x =>
          Tool.updateTests(rows)
        }.map { x =>
          ValidData(valid = true)
        }
      }
    }.map { x =>
      Ok(writeJs(x))
    }
  }

  def all = authAction(List(Role.SpecialMember, Role.Admin, Role.Member)).async { implicit request =>
    sampleDao.selectAll.map { x =>
      val array = x.map { x =>
        writeJs(x)
      }
      Ok(writeJs(array))
    }
  }

  def delete = adminAuth.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrData])
    val sampleIdF = sampleIdDao.delete(data.id)
    val sampleF = sampleDao.deleteById(data.id).map { x =>
      val sampleIdDir = Tool.getSampleIdDir(data.id)
      sampleIdDir.deleteQuietly
    }
    val testF = testDao.deleteAll(data.id)
    sampleIdF.zip(sampleF).zip(testF).map { x =>
      val response = ValidData(valid = true)
      Ok(writeJs(response))
    }
  }

  def getById = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrData])
    sampleDao.selectById(data.id).map { x =>
      val imagesDir = Tool.getImagesDir(x.id)
      val images = imagesDir.myListFiles.map(_.getName).filterNot { x =>
        x == "run.sh" || x.startsWith("thumb_")
      }
      val json = writeJs(x).toObj ++ x.basicInfo.toObj ++ x.freezerInfo.toObj ++ ujson.Obj("serverImages" -> images)
      Ok(json)
    }
  }

  def getOpById = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrData])
    sampleDao.selectByIdOp(data.id).map { xOp =>
      val sampleInfoOp = xOp.map { x =>
        val imagesDir = Tool.getImagesDir(x.id)
        val images = imagesDir.myListFiles.map(_.getName).filterNot { x =>
          x == "run.sh" || x.startsWith("thumb_")
        }
        val json = writeJs(x).toObj ++ x.basicInfo.toObj ++ x.freezerInfo.toObj ++ ujson.Obj("serverImages" -> images)
        json.transform(reader[SampleInfo])
      }
      Ok(writeJs(sampleInfoOp))
    }
  }

  def getByIdNoImage = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrData])
    sampleDao.selectById(data.id).map { x =>
      val finalSample = x.transformInto[SampleInfoNoImages]
      Ok(writeJs(finalSample))
    }
  }

  def update = Action.async(parse.multipartFormData) { implicit request =>
    val data = read[SampleInfo](request.body.dataParts("sampleInfo").head)
    val freezerInfo = data.transformInto[FreezerInfo]
    sampleDao.existFreezer(data.id, freezerInfo).flatMap { exist =>
      if (exist) {
        Future {
          Ok(ujson.Obj("valid" -> false, "message" -> "该冰箱信息已被使用！"))
        }
      } else {
        sampleDao.selectById(data.id).flatMap { dbX =>
          val curImagesDir = Tool.getCurImagesDir(data.id)
          val imagesDir = Tool.getImagesDir(data.id)
          data.serverImages.zipWithIndex.foreach { case (imageName, curI) =>
            new File(imagesDir, imageName).copyTo(new File(curImagesDir, s"image${curI}.${imageName.fileNameSuffix}"))
          }
          data.images.zipWithIndex.foreach { case (x, tmpI) =>
            val curI = data.serverImages.size + tmpI
            val strs = x.split(",")
            val extension = strs.head.split("/").last.split(";").head
            val imageFile = new File(curImagesDir, s"image${curI}.${extension}")
            val base64 = strs.last
            val imageBytes = DatatypeConverter.parseBase64Binary(base64)
            val img = ImageIO.read(new ByteArrayInputStream(imageBytes))
            ImageIO.write(img, extension, imageFile)
          }
          imagesDir.deleteQuietly
          curImagesDir.renameTo(imagesDir)
          Tool.productThumb(imagesDir)
          val basicInfoData = data.transformInto[BasicInfo]

          val labAppendDir = Tool.getLabAppendDir(data.id).createDirectoryWhenNoExist
          val labServerFiles = data.lab.files
          labAppendDir.myListFiles.foreach { file =>
            if (!labServerFiles.contains(file.getName)) {
              file.deleteQuietly
            }
          }
          val files = Tool.filesMoveDir(s"labFiles", labAppendDir)
          val fileNames = files.map(_.getName)
          val lab = data.lab.modify(_.files).using(_ ::: fileNames)
          val row = dbX.copy(updateTime = new DateTime(),
            basicInfo = writeJs(basicInfoData), tests = writeJs(data.tests), freezerInfo = writeJs(freezerInfo),
            lab = writeJs(lab))
          sampleDao.update(row).flatMap { x =>
            Tool.updateTests(List(row))
          }.map { x =>
            val response = ValidData(valid = true)
            Ok(writeJs(response))
          }
        }
      }
    }
  }

  def updateWareHouse = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[SampleInfo])
    val freezerInfo = data.transformInto[FreezerInfo]
    sampleDao.existFreezer(data.id, freezerInfo).flatMap { exist =>
      if (exist) {
        Future {
          Ok(ujson.Obj("valid" -> false, "message" -> "该冰箱信息已被使用！"))
        }
      } else {
        sampleDao.selectById(data.id).flatMap { dbX =>
          val dbFreezerInfo = dbX.freezerInfo.transform(reader[FreezerInfo])
          val newFreezerInfo = freezerInfo.modify(_.comments).setTo(dbFreezerInfo.comments)
          val row = dbX.copy(updateTime = new DateTime(),
            freezerInfo = writeJs(newFreezerInfo))
          sampleDao.update(row).map { x =>
            val response = ValidData(valid = true)
            Ok(writeJs(response))
          }
        }
      }
    }
  }

  def idCheck = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrData])
    sampleDao.idExist(data.id).map { b =>
      val response = if (b) {
        ValidData(valid = false)
      } else {
        ValidData(valid = true)
      }
      Ok(writeJs(response))
    }
  }

  def idsCheck = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrsData])
    sampleDao.existIds(data.ids).map { ids =>
      val response = IdsCheck.Response(existIds = ids)
      Ok(writeJs(response))
    }
  }

  def viewFile = Action { implicit request =>
    val data = Tool.transformRq.transform(reader[ViewAppend.Request])
    val folderDir = new File(Tool.getAppendIdDir(data.sampleId, data.testId), data.folder)
    val file = new File(folderDir, data.fileName)
    Ok.sendFile(file)
  }

  def downloadFile = Action { implicit request =>
    val data = Tool.transformRq.transform(reader[ViewAppend.Request])
    val folderDir = new File(Tool.getAppendIdDir(data.sampleId, data.testId), data.folder)
    val file = new File(folderDir, data.fileName)
    Ok.sendFile(file).withHeaders(
      CONTENT_DISPOSITION -> s"${
        Tool.getContentDisposition(file.getName)
      }",
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def viewLabFile = Action { implicit request =>
    val data = Tool.transformRq.transform(reader[ViewLabAppend.Request])
    val folderDir = Tool.getLabAppendDir(data.sampleId)
    val file = new File(folderDir, data.fileName)
    Ok.sendFile(file)
  }

  def downloadLabFile = Action { implicit request =>
    val data = Tool.transformRq.transform(reader[ViewLabAppend.Request])
    val folderDir = Tool.getLabAppendDir(data.sampleId)
    val file = new File(folderDir, data.fileName)
    Ok.sendFile(file).withHeaders(
      CONTENT_DISPOSITION -> s"${
        Tool.getContentDisposition(file.getName)
      }",
      CONTENT_TYPE -> "application/x-download"
    )
  }

  def getAllNoUsedId = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[GetAllNoUsedId.Request])
    val pageSize = data.pageSize
    sampleIdDao.selectAllId.zip(sampleDao.selectAllId).map { case (ids, usedIds) =>
      ids.filterNot { x =>
        usedIds.contains(x)
      }.filter { x =>
        x.contains(data.q)
      }
    }.map { x =>
      val trueX = x.sorted.sortWith((t, z) => {
        (data.q.size.toDouble / t.size) > (data.q.size.toDouble / z.size)
      }).drop(data.hasShowNum).take(pageSize)
      val results = trueX.map { y =>
        GetAllNoUsedId.Item(id = y, text = y)
      }
      val more = (data.hasShowNum + data.pageSize) < x.size
      val response = GetAllNoUsedId.Response(results = results, pagination = GetAllNoUsedId.More(more = more))
      Ok(writeJs(response))
    }
  }

  def freezerInfo = Action.async { implicit request =>
    val data = PlayJson.transform(request.body.asJson.get, reader[FreezerInfo])
    sampleDao.selectAll(data).map { rows =>
      val response = rows.map { row =>
        val freezerInfo = row.freezerInfo.transform(reader[FreezerInfo])
        FreezerInfo.SResponse(x = freezerInfo.rowNumber.toInt, y = freezerInfo.colNumber.toInt,
          updateTime = row.updateTime.toString(Tool.pattern), sampleId = row.id)
      }
      Ok(writeJs(response))
    }
  }

  def allByIds = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[AllByIds.Request])
    sampleDao.selectAll(data.ids).map { x =>
      val array = x.map { x =>
        val imagesDir = Tool.getImagesDir(x.id)
        val images = imagesDir.myListFiles.filterNot { x =>
          x.getName == "run.sh" || x.getName.startsWith("thumb_")
        }.map(_.base64)
        writeJs(x).toObj ++ x.basicInfo.toObj ++ x.freezerInfo.toObj ++ ujson.Obj("images" -> images) ++
          ujson.Obj("tests" -> x.tests)
      }
      Ok(writeJs(array))
    }
  }

  def lock = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrData])
    sampleDao.selectById(data.id).flatMap { x =>
      val newX = x.modify(_.locked).setTo(true)
      sampleDao.update(newX)
    }.map { x =>
      Ok(ujson.Str("success"))
    }
  }

  def unlock = Action.async { implicit request =>
    val data = Tool.transformRq.transform(reader[IdStrData])
    sampleDao.selectById(data.id).flatMap { x =>
      val newX = x.modify(_.locked).setTo(false)
      sampleDao.update(newX)
    }.map { x =>
      Ok(ujson.Str("success"))
    }
  }


}
